/**
 * Copyright 2013 Ministerio de Industria, EnergÃ­a y Turismo
 *
 * Este fichero es parte de "Componentes de Firma XAdES 1.1.7".
 *
 * Licencia con arreglo a la EUPL, VersiÃ³n 1.1 o â€“en cuanto sean aprobadas por la ComisiÃ³n Europeaâ€“ versiones posteriores de la EUPL (la Licencia);
 * Solo podrÃ¡ usarse esta obra si se respeta la Licencia.
 *
 * Puede obtenerse una copia de la Licencia en:
 *
 * http://joinup.ec.europa.eu/software/page/eupl/licence-eupl
 *
 * Salvo cuando lo exija la legislaciÃ³n aplicable o se acuerde por escrito, el programa distribuido con arreglo a la Licencia se distribuye Â«TAL CUALÂ»,
 * SIN GARANTÃ�AS NI CONDICIONES DE NINGÃšN TIPO, ni expresas ni implÃ­citas.
 * VÃ©ase la Licencia en el idioma concreto que rige los permisos y limitaciones que establece la Licencia.
 */
package es.mityc.javasign.xades.examples;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.StringWriter;
import java.security.KeyStore;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.NoSuchProviderException;
import java.security.PrivateKey;
import java.security.Provider;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
import java.util.Enumeration;
import java.util.List;

import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerException;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;

import org.w3c.dom.Document;
import org.xml.sax.SAXException;

import es.mityc.firmaJava.libreria.utilidades.UtilidadTratarNodo;
import es.mityc.firmaJava.libreria.xades.DataToSign;
import es.mityc.firmaJava.libreria.xades.FirmaXML;
import es.mityc.javasign.issues.PassStoreKS;
import es.mityc.javasign.pkstore.CertStoreException;
import es.mityc.javasign.pkstore.IPKStoreManager;
import es.mityc.javasign.pkstore.keystore.KSStore;

/**
 * <p>
 * Clase base que deberÃ­an extender los diferentes ejemplos para realizar
 * firmas XML.
 * </p>
 * 
 */
public abstract class GenericXMLSignature {

	/**
	 * <p>
	 * AlmacÃ©n PKCS12 con el que se desea realizar la firma
	 * </p>
	 */
	// public final static String PKCS12_RESOURCE = "/examples/usr0061.p12";
	public final static String PKCS12_RESOURCE = "C:/sura/certificado/Certificados-eSign-AfpCapital/certificado_jsanhueza/certificado_jsanhueza.pfx";

	/**
	 * <p>
	 * ConstraseÃ±a de acceso a la clave privada del usuario
	 * </p>
	 */
	// public final static String PKCS12_PASSWORD = "usr0061";
	public final static String PKCS12_PASSWORD = "1234";

	/**
	 * <p>
	 * Directorio donde se almacenarÃ¡ el resultado de la firma
	 * </p>
	 */
	public final static String OUTPUT_DIRECTORY = ".";

	/**
	 * <p>
	 * EjecuciÃ³n del ejemplo. La ejecuciÃ³n consistirÃ¡ en la firma de los
	 * datos creados por el mÃ©todo abstracto <code>createDataToSign</code>
	 * mediante el certificado declarado en la constante
	 * <code>PKCS12_FILE</code>. El resultado del proceso de firma serÃ¡
	 * almacenado en un fichero XML en el directorio correspondiente a la
	 * constante <code>OUTPUT_DIRECTORY</code> del usuario bajo el nombre
	 * devuelto por el mÃ©todo abstracto <code>getSignFileName</code>
	 * </p>
	 */
	protected void execute() {

		// Obtencion del gestor de claves
		IPKStoreManager storeManager = getPKStoreManager();
		if (storeManager == null) {
			System.err
					.println("El gestor de claves no se ha obtenido correctamente.");
			return;
		}

		// Obtencion del certificado para firmar. Utilizaremos el primer
		// certificado del almacen.
		X509Certificate certificate = getFirstCertificate(storeManager);
		if (certificate == null) {
			System.err.println("No existe ningÃºn certificado para firmar.");
			return;
		}

		// ObtenciÃ³n de la clave privada asociada al certificado

		PrivateKey privateKey;
		System.out.println("/certificado seleccionado: " + certificate);

		try {
			System.out
					.println("////////////////////////Privatekey//////////////////////////// ");
			privateKey = storeManager.getPrivateKey(certificate);
			System.out.println(privateKey);
		} catch (CertStoreException e) {
			System.err.println("Error al acceder al almacÃ©n.");
			return;
		}

		// ObtenciÃ³n del provider encargado de las labores criptogrÃ¡ficas
		Provider provider = storeManager.getProvider(certificate);

		/*
		 * CreaciÃ³n del objeto que contiene tanto los datos a firmar como la
		 * configuraciÃ³n del tipo de firma
		 */
		DataToSign dataToSign = createDataToSign();

		/*
		 * CreaciÃ³n del objeto encargado de realizar la firma
		 */
		FirmaXML firma = new FirmaXML();

		// Firmamos el documento
		Document docSigned = null;
		try {
			Object[] res = firma.signFile(certificate, dataToSign, privateKey,
					provider);
			docSigned = (Document) res[0];
		} catch (Exception ex) {
			System.err.println("Error realizando la firma");
			ex.printStackTrace();
			return;
		}

		// Guardamos la firma a un fichero en el home del usuario
		String filePath = OUTPUT_DIRECTORY + File.separatorChar
				+ getSignatureFileName();
		System.out.println("Firma salvada en en: " + filePath);
		saveDocumentToFile(docSigned, getSignatureFileName());
	}

	/**
	 * <p>
	 * Crea el objeto DataToSign que contiene toda la informaciÃ³n de la firma
	 * que se desea realizar. Todas las implementaciones deberÃ¡n proporcionar
	 * una implementaciÃ³n de este mÃ©todo
	 * </p>
	 * 
	 * @return El objeto DataToSign que contiene toda la informaciÃ³n de la
	 *         firma a realizar
	 */
	protected abstract DataToSign createDataToSign();

	/**
	 * <p>
	 * Nombre del fichero donde se desea guardar la firma generada. Todas las
	 * implementaciones deberÃ¡n proporcionar este nombre.
	 * </p>
	 * 
	 * @return El nombre donde se desea guardar la firma generada
	 */
	protected abstract String getSignatureFileName();

	/**
	 * <p>
	 * Escribe el documento a un fichero.
	 * </p>
	 * 
	 * @param document
	 *            El documento a imprmir
	 * @param pathfile
	 *            El path del fichero donde se quiere escribir.
	 */
	private void saveDocumentToFile(Document document, String pathfile) {
		try {
			FileOutputStream fos = new FileOutputStream(pathfile);
			UtilidadTratarNodo.saveDocumentToOutputStream(document, fos, true);
		} catch (FileNotFoundException e) {
			System.err.println("Error al salvar el documento");
			e.printStackTrace();
			System.exit(-1);
		}
	}

	/**
	 * <p>
	 * Escribe el documento a un fichero. Esta implementacion es insegura ya que
	 * dependiendo del gestor de transformadas el contenido podrÃ­a ser
	 * alterado, con lo que el XML escrito no serÃ­a correcto desde el punto de
	 * vista de validez de la firma.
	 * </p>
	 * 
	 * @param document
	 *            El documento a imprmir
	 * @param pathfile
	 *            El path del fichero donde se quiere escribir.
	 */
	@SuppressWarnings("unused")
	private void saveDocumentToFileUnsafeMode(Document document, String pathfile) {
		TransformerFactory tfactory = TransformerFactory.newInstance();
		Transformer serializer;
		try {
			serializer = tfactory.newTransformer();

			serializer.transform(new DOMSource(document), new StreamResult(
					new File(pathfile)));
		} catch (TransformerException e) {
			System.err.println("Error al salvar el documento");
			e.printStackTrace();
			System.exit(-1);
		}
	}

	/**
	 * <p>
	 * Devuelve el <code>Document</code> correspondiente al
	 * <code>resource</code> pasado como parÃ¡metro
	 * </p>
	 * 
	 * @param resource
	 *            El recurso que se desea obtener
	 * @return El <code>Document</code> asociado al <code>resource</code>
	 */
	protected Document getDocument(String resource) {
		Document doc = null;
		DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
		dbf.setNamespaceAware(true);
		try {
			doc = dbf.newDocumentBuilder().parse(
					this.getClass().getResourceAsStream(resource));
		} catch (ParserConfigurationException ex) {
			System.err.println("Error al parsear el documento");
			ex.printStackTrace();
			System.exit(-1);
		} catch (SAXException ex) {
			System.err.println("Error al parsear el documento");
			ex.printStackTrace();
			System.exit(-1);
		} catch (IOException ex) {
			System.err.println("Error al parsear el documento");
			ex.printStackTrace();
			System.exit(-1);
		} catch (IllegalArgumentException ex) {
			System.err.println("Error al parsear el documento");
			ex.printStackTrace();
			System.exit(-1);
		}
		return doc;
	}

	/**
	 * <p>
	 * Devuelve el contenido del documento XML correspondiente al
	 * <code>resource</code> pasado como parÃ¡metro
	 * </p>
	 * como un <code>String</code>
	 * 
	 * @param resource
	 *            El recurso que se desea obtener
	 * @return El contenido del documento XML como un <code>String</code>
	 */
	protected String getDocumentAsString(String resource) {
		Document doc = getDocument(resource);
		TransformerFactory tfactory = TransformerFactory.newInstance();
		Transformer serializer;
		StringWriter stringWriter = new StringWriter();
		try {
			serializer = tfactory.newTransformer();
			serializer.transform(new DOMSource(doc), new StreamResult(
					stringWriter));
		} catch (TransformerException e) {
			System.err.println("Error al imprimir el documento");
			e.printStackTrace();
			System.exit(-1);
		}

		return stringWriter.toString();
	}

	/**
	 * <p>
	 * Devuelve el gestor de claves que se va a utilizar
	 * </p>
	 * 
	 * @return El gestor de claves que se va a utilizar</p>
	 */
	private IPKStoreManager getPKStoreManager() {
		IPKStoreManager storeManager = null;
		try {
			/*
			 * KeyStore ks = KeyStore.getInstance("PKCS12");
			 * ks.load(this.getClass().getResourceAsStream(PKCS12_RESOURCE),
			 * PKCS12_PASSWORD.toCharArray()); storeManager = new KSStore(ks,
			 * new PassStoreKS(PKCS12_PASSWORD));
			 */

			/*
			 * KeyStore ks = KeyStore.getInstance("JKS");
			 * ks.load(this.getClass()
			 * .getResourceAsStream("C:/sura/keystore/clientcert.jks"), new
			 * String("P4ssw0rd.1").toCharArray()); storeManager = new
			 * KSStore(ks, new PassStoreKS("P4ssw0rd.1"));
			 */

			// --------------------------------------
			// String
			// keystorePath="C:/sura/certificado/Certificados-eSign-AfpCapital/certificado_jsanhueza/certificado_jsanhueza.pfx";
			// String keystorePassword="1234";

			/*
			 * 111316022015
			 * 
			 * String keystorePath =
			 * "C:/sura/certificado/keystore_Altiuz/clientcert.jks"; String
			 * keystorePassword="P4ssw0rd.1"; File file = new
			 * File(keystorePath); FileInputStream is = new
			 * FileInputStream(file);
			 * 
			 * KeyStore ks = KeyStore.getInstance(KeyStore.getDefaultType());
			 * ks.load(is, keystorePassword.toCharArray());
			 */

			KeyStore ks = null;
			ks = KeyStore.getInstance("Windows-MY");

			// Note: When a security manager is installed,
			// the following call requires SecurityPermission
			// "authProvider.SunMSCAPI".
			ks.load(null, null);
			storeManager = new KSStore(ks, null);

			/*
			 * String rucEmisor ="client"; Enumeration<String> enumeration =
			 * ks.aliases(); X509Certificate certificate = null; while
			 * (enumeration.hasMoreElements()) { String alias =
			 * enumeration.nextElement(); if (rucEmisor.equals(alias)) {
			 * certificate = (X509Certificate) ks.getCertificate(alias); break;
			 * } }
			 * 
			 * System.out.println("Certificate:"+certificate);
			 */

		} catch (KeyStoreException ex) {
			System.err.println("No se puede generar KeyStore PKCS12");
			ex.printStackTrace();
			System.exit(-1);
		} catch (NoSuchAlgorithmException ex) {
			System.err.println("No se puede generar KeyStore PKCS12");
			ex.printStackTrace();
			System.exit(-1);
		} catch (CertificateException ex) {
			System.err.println("No se puede generar KeyStore PKCS12");
			ex.printStackTrace();
			System.exit(-1);
		} catch (IOException ex) {
			System.err.println("No se puede generar KeyStore PKCS12");
			ex.printStackTrace();
			System.exit(-1);
		}
		return storeManager;
	}

	/**
	 * <p>
	 * Recupera el primero de los certificados del almacÃ©n.
	 * </p>
	 * 
	 * @param storeManager
	 *            Interfaz de acceso al almacÃ©n
	 * @return Primer certificado disponible en el almacÃ©n
	 */
	private X509Certificate getFirstCertificate(
			final IPKStoreManager storeManager) {
		List<X509Certificate> certs = null;
		try {

			certs = storeManager.getSignCertificates();

			// certs = storeManager.getTrustCertificates();//1
			// certs = storeManager.getPublicCertificates();//2

			System.out.println("CERT:" + certs);
		} catch (CertStoreException ex) {
			System.err.println("Fallo obteniendo listado de certificados");
			System.exit(-1);
		}
		if ((certs == null) || (certs.size() == 0)) {
			System.err.println("Lista de certificados vacÃ­a");
			System.exit(-1);
		}

		X509Certificate certificate = certs.get(1);

		return certificate;
	}

}
